(function() {
	var $ = require(L('$')), _ = require(L('_'));

	var ServiceHelper = {};
	ServiceHelper.BuildURL = function(serviceName, data) {
		if (serviceName == L('svUpdateMyAccount') || serviceName == L('svCreateUGD')|| serviceName == L('svEditUGD'))
			data = encodeURIComponent(data);
		return String.format('%s/%s?data=%s', L('url_api'), serviceName, data);
	};
	ServiceHelper.SendRequest = function(serviceName, data, callBack, cacheKey) {
		var retryTimes = 0;
		var dataInCache;
		if (cacheKey) {
			dataInCache = $.cache().cache_data.get(cacheKey);
			$.i(String.format('#CACHE : %s : %s', cacheKey, dataInCache));
		}
		//TODO:Base request
		var sendingData = JSON.parse(data);
		sendingData.DeviceID = $.deviceID();
		sendingData.ApiVersion = L('api_version');
		sendingData.MobilePlatform = L('ios_platform');
		sendingData.ServiceName = serviceName;
		sendingData.UserID = $.currentUserID() || -1;
		sendingData.IsCached = !$.isNull(dataInCache) ? true : false;
		sendingData.DeviceType = L('ios_platform');
		var url = ServiceHelper.BuildURL(serviceName, JSON.stringify(sendingData));
		$.i('#URL : ' + url);

		var httpClient = Ti.Network.createHTTPClient({
			onload : function(e) {
				var _reponseText = this.responseText;
				$.i('#RESPONSE : ' + _reponseText);
				var response = JSON.parse(_reponseText);
				if (response) {
					if (cacheKey) {
						if (response.IsNew) {
							_.defer(function() {
								$.cache().cache_data.insert_or_update(cacheKey, _reponseText);
							});
							callBack && callBack(_reponseText);
						} else {
							callBack && callBack(dataInCache, true);
						}
					} else {
						callBack && callBack(_reponseText);
					}
				}
			},
			onerror : function(e) {
				$.i(url);
				if (retryTimes > 0) {
					_.delay(function() {
						$.i('#ERROR, RETRY...');
						httpClient.open('GET', url);
						httpClient.send();
					}, 500);
					retryTimes--;
				} else {
					$.i(String.format('#RETRY %s TIMES ALSO FAIL :( ...', retryTimes));
					$.mask(false);
					if (serviceName != L('svDeleteNotifications') && serviceName != L('svDeleteWishlistList') && serviceName != L('svNearLocationsDetected') && serviceName != L('svDeleteUGD'))
						$.a(L('err_api_general'), L('message_error_title'));
					callBack && callBack(_reponseText);
				}
			},
			timeout : 120000
		});
		httpClient.open('GET', url);
		httpClient.send();
	};
	ServiceHelper.SendSecureRequest = function(serviceName, data, callBack) {
		var method = 'POST';
		var isGet = false;
		var digestUrl = ServiceHelper.BuildURL('secure/' + serviceName, data);
		var nonce = L('digestNonce');
		var url = digestUrl.split('?data=')[0];
		var httpClient = Ti.Network.createHTTPClient();
		httpClient.timeout = 60000;
		httpClient.onload = function() {
			var status = this.status;
			if (status) {
				var _status = parseInt(status);
				if (_status >= 400) {
					$.a(L('err_api_general'), L('message_error_title'));
				} else {
					if (this.onloadCallback) {
						var response = JSON.parse(this.responseText);
						if ($.isNull(response.IsNew) || response.IsNew) {
							response.IsNew = false;
							this.onloadCallback(this.responseText);
							cache.dataCaching(serviceName, JSON.stringify(response), digestUrl.split('?data=')[1]);
						} else {
							var cachedResponseText = cache.findCaching(serviceName, digestUrl.split('?data=')[1]);
							this.onloadCallback(cachedResponseText);
						}
					}
				}
			}
		};
		httpClient.onerror = function(e) {
			if (this.status == 401) {
				ServiceHelper._sendAuthRequest(serviceName, e.source, method, digestUrl);
			} else {
				if (this.status !== 0) {
					$.a(L('err_api_general'), L('message_error_title'));
				}
				if (this.onerrorCallback) {
					this.onerrorCallback(e.error);
				}
			}
		}
		httpClient.onloadCallback = function(e) {
			callBack(e);
		};
		httpClient.onerrorCallback = function() {
			callBack(e);
		};
		//httpClient = ServiceHelper._resultRequest(httpClient, method, digestUrl, nonce);
		if (nonce != "undefined") {
			httpClient = ServiceHelper._resultRequest(httpClient, method, digestUrl, nonce);
		} else {
			httpClient.open(method, url);
		}
		httpClient.send(url.split('?data=')[1]);
	};
	ServiceHelper._sendAuthRequest = function(serviceName, httpClient, method, digestUrl) {
		var isGet = false;
		var requestUrl = isGet ? digestUrl : digestUrl.split('?data=')[0];
		var authReqSecond = ServiceHelper._resultAuthRequest(serviceName, httpClient, method, requestUrl);
		!isGet ? authReqSecond.setRequestHeader("enctype", "multipart/form-data") : '';
		var param = Ti.Network.decodeURIComponent(digestUrl.split('?data=')[1]);
		isGet ? authReqSecond.send() : authReqSecond.send(param);
	};
	ServiceHelper._resultRequest = function(httpClient, method, digestUrl, nonce) {
		var response = ServiceHelper.clientResponse(digestUrl, L('consumerKey'), L('consumerSecret'), nonce, method);
		return ServiceHelper._createRequest(httpClient, L('consumerKey'), nonce, response, digestUrl, method);
	};
	ServiceHelper._resultAuthRequest = function(serviceName, httpClient, method, digestUrl) {
		var header = httpClient.getResponseHeader('Www-Authenticate');
		var nonce = ServiceHelper.getNonceString(header);
		var response = ServiceHelper.clientResponse(digestUrl, L('consumerKey'), L('consumerSecret'), nonce, method);
		// PublicFunctions.digestNonce = nonce;

		return ServiceHelper._createAuthRequest(serviceName, L('consumerKey'), nonce, response, digestUrl, method, httpClient.onloadCallback, httpClient.onerrorCallback);
	};
	ServiceHelper.getNonceString = function(header) {
		var nonceArray = header.split(',');
		var nonce = nonceArray[1].split('nonce=')[1].replace('"', '');
		return nonce = nonce.replace('"', '');
	};
	ServiceHelper.getAuthorizationString = function(username, nonce, digestURL, response) {
		return "Digest username=\"" + username + "\", realm=\"" + L('authRealm') + "\", nonce=\"" + nonce + "\", uri=\"" + digestURL + "\", response=\"" + response + "\", qop=\"auth\", nc=00000001, cnonce=0";
	};
	ServiceHelper.clientResponse = function(digestURL, username, password, nonce, method) {
		var ha1 = Ti.Utils.md5HexDigest(username + ":" + L('authRealm') + ":" + password);
		var ha2 = Ti.Utils.md5HexDigest(method + ":" + digestURL);
		var responseStringToMD5Encode = ha1 + ":" + nonce + ":00000001:0:auth:" + ha2;
		var response = Ti.Utils.md5HexDigest(responseStringToMD5Encode);
		return response;
	};
	ServiceHelper._createRequest = function(httpClient, username, nonce, response, digestUrl, method) {
		var authorization = ServiceHelper.getAuthorizationString(username, nonce, digestUrl, response);
		$.i('#authorization : ' + authorization);
		httpClient.open(method, digestUrl);
		$.i('#DIGEST_URL : ' + digestUrl);
		httpClient.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
		httpClient.setRequestHeader('Authorization', authorization);
		$.i('#DIGEST_HTTP : ' + JSON.stringify(httpClient));
		return httpClient;
	};
	ServiceHelper._createAuthRequest = function(serviceName, username, nonce, response, digestUrl, method, onloadCallback, onerrorCallback) {
		var authorization = ServiceHelper.getAuthorizationString(username, nonce, digestUrl, response);
		var httpClient = Ti.Network.createHTTPClient({
			onload : function() {
				if (this.onloadCallback) {
					this.onloadCallback(this.responseText);
					cache.dataCaching(serviceName, this.responseText);
				}
			},
			onerror : function(e) {
				if (this.onerrorCallback) {
					this.onerrorCallback(e.error);
				}
			}
		});
		httpClient.onloadCallback = onloadCallback;
		httpClient.onerrorCallback = onerrorCallback;
		httpClient.open(method, digestUrl);
		httpClient.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
		httpClient.setRequestHeader('Authorization', authorization);
		return httpClient;
	};

	//================================================================================================================
	var webservice = {};
	//Security Requests	
	webservice.secureLogin = function(userName, userAuthToken, callBack) {
		var data = {
			"UserName" : userName,
			"UserAuthToken" : userAuthToken,
			"DeviceAPID" : '',
			"DeviceType" : L('ios_platform'),
			"DeviceID" : $.deviceID(),
			"ApiVersion" : L('api_version')
		};
		ServiceHelper.SendSecureRequest(L('svSecuredLogin'), JSON.stringify(data), callBack);
	};
	webservice.securedValidateLogin = function(userName, userAuthToken, callBack) {
		var data = {
			"UserName" : userName,
			"UserAuthToken" : userAuthToken
		};
		ServiceHelper.SendSecureRequest(L('svSecuredValidateLogin'), JSON.stringify(data), callBack);
	};

	//Non-Security Requests
	webservice.getInitData = function(callBack) {
		var data = {};
		ServiceHelper.SendRequest(L('svGetInitData'), JSON.stringify(data), callBack);
	};
	webservice.getStaticContent = function(callBack) {
		var data = {};
		ServiceHelper.SendRequest(L('svGetStaticContent'), JSON.stringify(data), callBack, L('svGetStaticContent'));
	};
	webservice.updateMobileAppSetting = function(allowReceive, callBack) {
		var data = {
			'AllowReceiveRetailerNotification' : allowReceive
		};
		ServiceHelper.SendRequest(L('svUpdateMobileAppSetting'), JSON.stringify(data), callBack);
	};

	webservice.validateRegisterData = function(email, shareCode, callBack) {
		var data = {
			"Email" : email,
			"ShareCode" : shareCode
		};
		ServiceHelper.SendRequest(L('svValidateRegisterData'), JSON.stringify(data), callBack);
	};
	webservice.register = function(firstName, lastName, email, password, shareCode, profileName, callBack) {
		var data = {
			"FbUID" : '',
			"FirstName" : firstName,
			"LastName" : lastName,
			"Email" : email,
			"Password" : password,
			"DOB" : '',
			"Gender" : 0,
			"MobileNumber" : '',
			"ShareCode" : shareCode,
			"ProfileName":profileName,
			"DeviceAPID" : ''
		};
		ServiceHelper.SendRequest(L('svRegister'), JSON.stringify(data), callBack);
	};
	webservice.login = function(userName, password, callBack) {
		var data = {
			"UserName" : userName,
			"Password" : password
		};
		ServiceHelper.SendRequest(L('svLogin'), JSON.stringify(data), callBack);
	};
	webservice.validateUpdateMyAccount = function(userName, userAuthToken, shareCode, callBack) {
		var data = {
			"UserName" : userName,
			"UserAuthToken" : userAuthToken,
			"ShareCode" : shareCode,
			"ApiVersion" : L('api_version')
		};
		ServiceHelper.SendSecureRequest(L('svValidateUpdateMyAccount'), JSON.stringify(data), callBack);
	};
	webservice.updateMyAccount = function(firstName, lastName, email, password, newPassword, dOB, gender, mobileNumber, shareCode, address, suburb, state, postcode, countryID, profileName, callBack) {
		var data = {
			"FirstName" : firstName,
			"LastName" : lastName,
			"Email" : email,
			"Password" : password,
			"NewPassword" : newPassword,
			"DOB" : dOB,
			"Gender" : gender,
			"MobileNumber" : mobileNumber,
			"ShareCode" : shareCode,
			"Address" : address,
			"Suburb" : suburb,
			"State" : state,
			"Postcode" : postcode,
			"CountryID" : countryID,
			"ProfileName" : profileName
		};
		ServiceHelper.SendRequest(L('svUpdateMyAccount'), JSON.stringify(data), callBack);
	};
	webservice.checkExistedFBAccount = function(fbUid, callBack) {
		var data = {
			'FbUid' : fbUid
		};
		ServiceHelper.SendRequest(L('svCheckExistedFBAccount'), JSON.stringify(data), callBack);
	};
	webservice.emailNewPassword = function(firstName, email, callBack) {
		var data = {
			"FirstName" : firstName,
			"Email" : email
		};
		ServiceHelper.SendRequest(L('svEmailNewPassword'), JSON.stringify(data), callBack);
	};
	webservice.getPurchaseRetailers = function(callBack) {
		var data = {};
		ServiceHelper.SendRequest(L('svGetPurchaseRetailers'), JSON.stringify(data), callBack);
	};

	webservice.getNotifications = function(callBack) {
		var data = {};
		var cacheKey = String.format('%s/%s', L('svGetNotifications'), $.currentUserID());
		ServiceHelper.SendRequest(L('svGetNotifications'), JSON.stringify(data), callBack, cacheKey);
	};
	webservice.loginByFB = function(fbUid, firstName, lastName, callBack) {
		var data = {
			"FbUid" : fbUid,
			"FirstName" : firstName,
			"LastName" : lastName,
			"DeviceAPID" : ''
		};
		ServiceHelper.SendRequest(L('svLoginByFB'), JSON.stringify(data), callBack);
	};
	webservice.getUserDetails = function(callBack) {
		var data = {};
		var cacheKey = String.format('%s/%s', L('svGetUserDetails'), $.currentUserID());
		ServiceHelper.SendRequest(L('svGetUserDetails'), JSON.stringify(data), callBack, cacheKey);
	};
	webservice.getRetailersUserShopping = function(callBack) {
		var data = {};
		ServiceHelper.SendRequest(L('svGetRetailersUserShopping'), JSON.stringify(data), callBack);
	};

	webservice.getMyDesires = function(callBack) {
		var data = {};
		var cacheKey = String.format('%s/%s', L('svGetMyDesires'), $.currentUserID());
		ServiceHelper.SendRequest(L('svGetMyDesires'), JSON.stringify(data), callBack, cacheKey);
	};
	webservice.deleteWishlistList = function(wishlistID, callBack) {
		var userIDs = new Array();
		userIDs.push($.currentUserID());
		var ids = new Array();
		ids.push(wishlistID);
		var data = {
			"UserIDs" : userIDs,
			"WishlistIDs" : ids
		};
		ServiceHelper.SendRequest(L('svDeleteWishlistList'), JSON.stringify(data), callBack);
	};
	webservice.getProductDetails = function(productVariantID, retailerStoreID, isScan, callBack) {
		var data = {
			'productVariantID' : productVariantID,
			'RetailerStoreID' : retailerStoreID,
			'IsScanned' : isScan
		};
		var cacheKey = L('svGetProductDetails') + '/' + $.currentUserID() + '/' + productVariantID + '/' + retailerStoreID;
		ServiceHelper.SendRequest(L('svGetProductDetails'), JSON.stringify(data), callBack, cacheKey);
	};
	webservice.getProductVariantByBarcode = function(barcode, callback) {
		var data = {
			"Barcode" : barcode
		}
		ServiceHelper.SendRequest(L('svGetProductByBarcode'), JSON.stringify(data), callback);
	};

	webservice.getShoppingCartID = function(callBack) {
		var data = {}
		ServiceHelper.SendRequest(L('svGetShoppingCart'), JSON.stringify(data), callBack);
	};
	webservice.addToCart = function(productVariantID, retailerStoreID, saleCampaignOfferCustomerDefinedBundleID, callBack) {
		productVariantID = $.isNull(saleCampaignOfferCustomerDefinedBundleID) ? productVariantID : '';
		var data = {
			'ProductVariantID' : productVariantID,
			'RetailerStoreID' : retailerStoreID,
			'SaleCampaignOfferCustomerDefinedBundleID' : saleCampaignOfferCustomerDefinedBundleID,
			'Quantity' : 1
		};
		ServiceHelper.SendRequest(L('svAddToCart'), JSON.stringify(data), callBack);
	};
	webservice.getPurchases = function(callBack) {
		var data = {};
		var cacheKey = String.format('%s/%s', L('svGetPurchases'), $.currentUserID());
		ServiceHelper.SendRequest(L('svGetPurchases'), JSON.stringify(data), callBack, cacheKey);
	};

	webservice.getNotifications = function(callBack) {
		var data = {};
		var cacheKey = String.format('%s/%s', L('svGetNotifications'), $.currentUserID());
		ServiceHelper.SendRequest(L('svGetNotifications'), JSON.stringify(data), callBack, cacheKey);
	};
	webservice.getNotificationDetails = function(notificationID, callBack) {
		var data = {
			'NotificationID' : notificationID
		};
		var cacheKey = L('svGetNotificationDetails') + '/' + notificationID;
		ServiceHelper.SendRequest(L('svGetNotificationDetails'), JSON.stringify(data), callBack, cacheKey);
	};
	webservice.deleteNotifications = function(notificationIDs, callBack) {
		var data = {
			"NotificationIDs" : notificationIDs
		}
		ServiceHelper.SendRequest(L('svDeleteNotifications'), JSON.stringify(data), callBack);
	};
	webservice.shareViaEmail = function(senderEmail, receivers, productVariantID, retailerStoreID, coreProductVariantID, notificationID, callBack) {
		var data = {
			'SenderEmail' : senderEmail,
			'Receivers' : receivers,
			'ProductVariantID' : productVariantID || '',
			'RetailerStoreID' : retailerStoreID || '',
			'CoreProductVariantID' : coreProductVariantID || '',
			'NotificationID' : notificationID || ''
		};
		ServiceHelper.SendRequest(L('svShareViaEmail'), JSON.stringify(data), callBack);
	};
	webservice.updateShareInfo = function(productVariantID, retailerStoreID, productShareType, coreProductVariantID, callBack) {
		var data = {
			"ProductVariantID" : productVariantID,
			"RetailerStoreID" : retailerStoreID,
			"ShareTimes" : 1,
			"CoreProductVariantID" : coreProductVariantID,
			"ProductShareType" : productShareType
		};
		ServiceHelper.SendRequest(L('svUpdateShareInfo'), JSON.stringify(data), callBack);
	};
	webservice.getAllRetailerStore = function(callback) {
		var data = {};

		ServiceHelper.SendRequest(L('svGetAllRetailerStore'), JSON.stringify(data), callback);
	};
	webservice.nearLocationsDetected = function(locationList, callback) {
		var data = {
			"NearLocations" : locationList,
		};
		ServiceHelper.SendRequest(L('svNearLocationsDetected'), JSON.stringify(data), callback);
	};
	webservice.createUGD = function(retailerName, retailerStoreName, retailerID, retailerStoreID, price, comment, isSubmitted, sku, media, callback) {
		var data = {
			"RetailerName" : retailerName,
			"RetailerStoreName" : retailerStoreName,
			"RetailerID" : retailerID,
			"RetailerStoreID" : retailerStoreID,
			"Price" : price,
			"Comment" : comment,
			"IsSubmitted" : isSubmitted,
			"SKU" : sku
		};
		data.DeviceID = $.deviceID();
		data.ApiVersion = L('api_version');
		data.MobilePlatform = L('ios_platform');
		data.ServiceName = L('svCreateUGD');
		data.UserID = $.currentUserID() || -1;
		data.IsCached = false;
		data.DeviceType = L('ios_platform');
		if ($.isOnline()) {
			$.mask();
			var xhr = Ti.Network.createHTTPClient();
			xhr.onerror = function(e) {
				$.mask(false);
			};
			xhr.onload = function(e) {
				$.mask(false);
				callback && callback(this.responseText);
			};
			xhr.timeout = 120000;
			var url = String.format("%s/%s?data=%s", L('url_api'), L('svCreateUGD'), JSON.stringify(data));
			$.w(url);
			xhr.open('POST', url);
			xhr.send({
				media : media
			});
		}
	};
	webservice.deleteUGD = function(desireID, callback) {
		var data = {
			"DesireID" : desireID,
		};
		ServiceHelper.SendRequest(L('svDeleteUGD'), JSON.stringify(data), callback);
	};
	webservice.editUGD = function(desireID, retailerName, retailerStoreName, retailerID, retailerStoreID, price, comment, isSubmitted, callback) {
		var data = {
			"DesireID" : desireID,
			"RetailerName" : retailerName,
			"RetailerStoreName" : retailerStoreName,
			"RetailerID" : retailerID,
			"RetailerStoreID" : retailerStoreID,
			"Price" : price,
			"Comment" : comment,
			"IsSubmitted" : isSubmitted
		};
		ServiceHelper.SendRequest(L('svEditUGD'), JSON.stringify(data), callback);
	};
	webservice.updateShareUGD = function(desireID, shareType, callback) {
		var data = {
			"DesireID" : desireID,
			"ShareType" : shareType
		};
		ServiceHelper.SendRequest(L('svUpdateShareUGD'), JSON.stringify(data), callback);
	};
	webservice.shareUGDViaEmail = function(desireID, senderEmail, receivers, callback) {
		var data = {
			"DesireID" : desireID,
			"SenderEmail" : senderEmail,
			"Receivers" : receivers
		};
		ServiceHelper.SendRequest(L('svShareUGDViaEmail'), JSON.stringify(data), callback);
	};

	module.exports = webservice;
})();
